package com.yt.phone;

import com.google.gson.Gson;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @name TangGnag
 * @time 2023-06-29 15:15
 */
@Component
public class SmsService {
    @Value("${sms.signName}")
    private String signName;
    @Value("${sms.code.templateCode}")
    private String codeTemplateCode;

    @Value("${sms.text.templateCode}")
    private String textTemplateCode;

    @Value("${sms.accessKey}")
    private String accessKey;
    @Value("${sms.securityKey}")
    private String securityKey;

    //天翼云短信发送错误码
    private static HashMap<String,String> codeInfoMap = new HashMap<>();

    //初始化错误码信息
    static {
        codeInfoMap.put("OK","OK");
        codeInfoMap.put("99999","系统内部错误");
        codeInfoMap.put("10001","包头ctyun-eop-ak错误");
        codeInfoMap.put("10002","包头ctyun-eop-ak错误");
        codeInfoMap.put("10003","包头ctyun-eop-ak错误");
        codeInfoMap.put("10004","IP禁止访问（白名单");
        codeInfoMap.put("10005","IP禁止访问（黑名单");
        codeInfoMap.put("10006","包头ctyun-eop-ak错误");
        codeInfoMap.put("10007","包头ctyun-eop-ak错误");
        codeInfoMap.put("10008","签名验签失败（MD5环节）");
        codeInfoMap.put("10009","签名验签失败（HMAC环节）");
        codeInfoMap.put("10012","流控限制");
        codeInfoMap.put("10013","签名验签失败（RSA）");
        codeInfoMap.put("10015","触发熔断");
        codeInfoMap.put("30001","param error");
        codeInfoMap.put("30003","鉴权失败");
        codeInfoMap.put("30004","没有记录");
        codeInfoMap.put("30005","已经有记录，请勿重复提交");
        codeInfoMap.put("30006","URL错误，或者响应状态码不是200x");
        codeInfoMap.put("30007","图片太大");
        codeInfoMap.put("30008","变量名不合法");
        codeInfoMap.put("30009","变量名与模板定义不一致");
        codeInfoMap.put("30010","缓存错误");
        codeInfoMap.put("30011","不支持的图片扩展");
        codeInfoMap.put("30012","错误的BASE64编码");
        codeInfoMap.put("30013","签名不存在或者未审核通过");
        codeInfoMap.put("30014","模板不存在或者未审核通过");
        codeInfoMap.put("30015","用户不存在或者没有开通短信业务");
        codeInfoMap.put("30016","黑名单不存在");
        codeInfoMap.put("30017","通道不存在");
        codeInfoMap.put("30018","导出记录不存在");
        codeInfoMap.put("30019","区域码不存在");
        codeInfoMap.put("30020","白名单不存在");
        codeInfoMap.put("30021","资源包没有余量");
        codeInfoMap.put("30022","欠费");
        codeInfoMap.put("30023","短链无记录或者删除");
        codeInfoMap.put("30024","签名不存在或者未审核");
        codeInfoMap.put("30025","签名为空");
        codeInfoMap.put("30026","签名已审核通过，不能删除");
        codeInfoMap.put("30027","扩展码已经被其他签名使用");
        codeInfoMap.put("30028","当签名选择为他用时，上传凭证至少2个");
        codeInfoMap.put("30029","手机号码为空");
        codeInfoMap.put("30030","手机号码错误");
        codeInfoMap.put("30031","黑名单用户");
        codeInfoMap.put("30032","模板编号为空");
        codeInfoMap.put("30033","模板没有记录");
        codeInfoMap.put("30034","变量不是JSON字符串");
        codeInfoMap.put("30035","变量错误");
        codeInfoMap.put("30036","模板已被审核");
        codeInfoMap.put("30037","超过设定日限制发送量");
        codeInfoMap.put("30038","超过设定月限制发送量");
        codeInfoMap.put("30039","超过单个手机号码日发送量");
        codeInfoMap.put("30040","超过单个手机号码每小时发送量");
        codeInfoMap.put("30041","超过单个手机号码每分钟发送量");
        codeInfoMap.put("30042","区域码错误");
        codeInfoMap.put("30043","时间错误");
        codeInfoMap.put("40001","个人用户，请重新认证为企业用户");
        codeInfoMap.put("40002","已订购免费套餐");
    }

    public void sendSmsCode(String phoneNumber,String code)throws Exception{
        SendSmsRequest sendSmsRequest = new SendSmsRequest();
        sendSmsRequest.setPhoneNumber(phoneNumber);
        //签名
        sendSmsRequest.setSignName(signName);
        //模板ID
        sendSmsRequest.setTemplateCode(codeTemplateCode);
        //验证码应当随机生成并于当前号码存储在缓存数据库中
        sendSmsRequest.setTemplateParam("{\"code\":\"" + code + "\"}");
        sendSms(sendSmsRequest);
    }

    public void sendSmsText(String phoneNumber,String content)throws Exception{
        SendSmsRequest sendSmsRequest = new SendSmsRequest();
        sendSmsRequest.setPhoneNumber(phoneNumber);
        //签名
        sendSmsRequest.setSignName(signName);
        //模板ID
        sendSmsRequest.setTemplateCode(textTemplateCode);
        //验证码应当随机生成并于当前号码存储在缓存数据库中
        sendSmsRequest.setTemplateParam("{\"content\":\"" + content + "\"}");
        sendSms(sendSmsRequest);
    }

    public  void sendSms(SendSmsRequest sendSmsRequest) throws Exception {
        // 构造body请求参数
        Map<String, String> params = buildParams(sendSmsRequest);
        String body =new Gson().toJson(params);

        try {
            // 构造时间戳
            SimpleDateFormat timeFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
            Date now = new Date();
            String signatureTime = timeFormat.format(now);
            String signatureDate = dateFormat.format(now);

            // 构造请求流水号
            String uuId = UUID.randomUUID().toString();

            // 构造待签名字符串
            String campHeader = String.format("ctyun-eop-request-id:%s\neop-date:%s\n", uuId, signatureTime);
            // header的key按照26字母进行排序, 以&作为连接符连起来
            URL url = new URL("https://sms-global.ctapi.ctyun.cn/sms/api/v1");
            String query = url.getQuery();
            StringBuilder afterQuery = new StringBuilder();
            if (query != null) {
                String[] param = query.split("&");
                Arrays.sort(param);
                for (String str : param) {
                    if (afterQuery.length() < 1)
                        afterQuery.append(str);
                    else
                        afterQuery.append("&").append(str);
                }
            }

            // 报文原封不动进行sha256摘要
            String calculateContentHash = getSHA256(body);
            String signatureStr = campHeader + "\n" + afterQuery + "\n" + calculateContentHash;

            // 构造签名
            byte[] kTime = hmacSHA256(signatureTime.getBytes(), securityKey.getBytes());
            byte[] kAk = hmacSHA256(accessKey.getBytes(), kTime);
            byte[] kDate = hmacSHA256(signatureDate.getBytes(), kAk);
            String signature = Base64.getEncoder().encodeToString(hmacSHA256(signatureStr.getBytes(StandardCharsets.UTF_8), kDate));


            // 构造请求头
            HttpPost httpPost = new HttpPost(String.valueOf(url));
            httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
            httpPost.setHeader("ctyun-eop-request-id", uuId);
            httpPost.setHeader("Eop-date", signatureTime);
            String signHeader = String.format("%s Headers=ctyun-eop-request-id;eop-date Signature=%s", accessKey, signature);
            httpPost.setHeader("Eop-Authorization", signHeader);

            httpPost.setEntity(new StringEntity(body, ContentType.create("application/json", "utf-8")));

            try (CloseableHttpClient httpClient = HttpClients.createDefault();
                 CloseableHttpResponse response = httpClient.execute(httpPost)) {
                String result = EntityUtils.toString(response.getEntity(), "utf-8");
                //将json字符串转换为对象
                Entity entity = new Gson().fromJson(result, Entity.class);
                //判断api响应是否正常
                if ("OK".equals(entity.getCode())){
                    //正常不做处理正常结束
                }else {
                    //响应代码异常则根据异常代码生成异常
                    if(codeInfoMap.containsKey(entity.getCode())){
                        throw new Exception("[天翼云系统]"+codeInfoMap.get(entity.getCode()));
                    }
                    else{
                        throw new Exception("[天翼云系统]未知错误");
                    }
                }
            }
        } catch (Exception e) {
            throw new Exception("[短信调用代码]"+e.getMessage());
        }
    }


    /**
     * 构造请求参数
     *
     * @param sendSmsRequest 请求参数
     * @return Map
     */
    private static Map<String, String> buildParams(SendSmsRequest sendSmsRequest) {
        Map<String, String> params = new HashMap<>(16);
        params.put("action", "SendSms");
        params.put("phoneNumber", sendSmsRequest.getPhoneNumber());
        params.put("signName", sendSmsRequest.getSignName());
        params.put("templateCode", sendSmsRequest.getTemplateCode());
        params.put("templateParam", sendSmsRequest.getTemplateParam());
        params.put("extendCode", sendSmsRequest.getExtendCode());
        params.put("sessionId", sendSmsRequest.getSessionId());
        return params;
    }

    private static String toHex(byte[] data) {
        StringBuilder sb = new StringBuilder(data.length * 2);
        byte[] var2 = data;
        int var3 = data.length;
        for (int var4 = 0; var4 < var3; ++var4) {
            byte b = var2[var4];
            String hex = Integer.toHexString(b);
            if (hex.length() == 1) {
                sb.append("0");
            } else if (hex.length() == 8) {
                hex = hex.substring(6);
            }
            sb.append(hex);
        }
        return sb.toString().toLowerCase(Locale.getDefault());
    }

    private static String getSHA256(String text) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            md.update(text.getBytes(StandardCharsets.UTF_8));
            return toHex(md.digest());
        } catch (NoSuchAlgorithmException var3) {
            return null;
        }
    }

    private static byte[] hmacSHA256(byte[] data, byte[] key) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(key, "HmacSHA256"));
            return mac.doFinal(data);
        } catch (Exception e) {
            return new byte[0];
        }
    }



}
